import pandas as pd
import shutil
from openpyxl import load_workbook
import os
from PySide2.QtWidgets import QMessageBox
# from tools.Area_Objects import areaObjs
from tools.Area_tools import Area_Tools



class ElecAccountServer:
    """
    电子记账汇总业务类
    """

    def __init__(self, preAccoutOverViewPath, accoutOverViewPath, accoutReviewPath):
        """
        初始化方法
        :param preAccoutOverViewPath: 前月记账概况路径
        :param accoutOverViewPath: 当月记账概览路径
        :param accoutReviewPath: 当月账页复核路径
        """

        # 获取当月记账概览
        accoutOverViewDf = pd.read_csv(accoutOverViewPath, encoding="GBK")
        # 获取记账概览的调查小区ID
        accoutOverViewDf['调查小区ID'] = accoutOverViewDf["住户ID"].str[0:15]
        # 前月记账概览
        preAccoutOverViewDf = pd.read_csv(preAccoutOverViewPath, encoding="GBK")
        # 获取记账复核
        accoutReviewDf = pd.read_csv(accoutReviewPath, encoding="GBK")
        # 小区样本 '../data/电子记账/6月/小区样本5105.243.20240723.174312 - 副本.csv'
        areaSampleDf = pd.read_csv("./res/电子记账访户初始化数据/小区样本.csv", encoding="GBK")
        # 将小区样本的调查小区ID设置为str
        areaSampleDf['调查小区ID'] = areaSampleDf['调查小区ID'].astype(str)
        # 获取电子记账的月份
        self.month = int(accoutReviewPath[accoutReviewPath.find('5105.') + 9:accoutReviewPath.find('-')])
        # 当前月 电子记账的月份 导出月份
        self.preMonth = (self.month + 1) % 12
        # 获取导出的日历
        self.calada = accoutReviewPath[accoutReviewPath.find('-') + 1:len(accoutReviewPath) - 4][7:15]

        ## 基本数据处理

        # 组装城镇农村标识的记账概览
        self.accoutOverView = pd.merge(accoutOverViewDf,
                                       areaSampleDf[['调查小区ID', '分省框城乡类别']].copy(), how='inner',
                                       on=['调查小区ID']).copy()
        # 共同户记账笔数差
        self.comonHouses = pd.merge(self.accoutOverView,
                                    preAccoutOverViewDf[['住户ID', '记账条数']].rename(
                                        columns={'记账条数': '前月记账条数'}),
                                    how='inner', on=['住户ID']).copy()
        self.comonHouses["记账笔数差"] = (self.comonHouses['记账条数'] - self.comonHouses['前月记账条数']).abs()

        # 格式化账页复核的日期
        accoutReviewDf['创建时间'] = accoutReviewDf['创建时间'].astype('datetime64[ns]')
        accoutReviewDf['记账日期'] = accoutReviewDf['记账日期'].astype('datetime64[ns]')
        accoutReviewDf['记账时间差'] = (
            (accoutReviewDf['创建时间'] - accoutReviewDf['记账日期']).dt.days).abs()
        # 生成账页复核的区县码
        accoutReviewDf['COUN'] = accoutReviewDf['SID'].str[0:6]
        self.accoutReviewDf = accoutReviewDf

    def getElecAccountSituation(self, accoutOverView, comonHouses, accoutReview):
        """
        获取区县电子所有电子记账情况
        :param accoutOverView: 账页概览数据表
        :param comonHouses: 账页概览, 共同户表(包含记账笔数)
        :param accoutReview: 账页复核
        :return: 每个区县的记账项目数据
        """
        res = []
        elecDataDf = accoutOverView[
            (accoutOverView['是否电子记账户'] == '是') & (accoutOverView['激活状态'] == '已激活')].copy()
        accoutOverViewCit = elecDataDf[~elecDataDf['分省框城乡类别'].isin(['R'])].copy()
        accoutOverViewCout = elecDataDf[elecDataDf['分省框城乡类别'].isin(['R'])].copy()
        accoutReviewEle = accoutReview[accoutReview['记账方式'] == '移动'].copy()
        # 样本量
        ## 全体
        res.append(accoutOverView.shape[0])
        ## 城镇
        res.append(accoutOverView[~accoutOverView['分省框城乡类别'].isin(['R'])].shape[0])
        ## 农村
        res.append(accoutOverView[accoutOverView['分省框城乡类别'].isin(['R'])].shape[0])

        # 电子记账激活使用情况
        ## 全体
        res.append(elecDataDf.shape[0])
        ## 城镇
        res.append(accoutOverViewCit.shape[0])
        ## 农村
        res.append(accoutOverViewCout.shape[0])
        ## 全体电子记账率（%）
        res.append(res[3] / res[0] * 100)
        ## 城镇电子记账率（%）
        res.append(res[4] / res[1] * 100)
        ## 农村电子记账率（%）
        res.append(res[5] / res[2] * 100)

        # 电子记账达标户情况
        ## 全体
        ## 城镇数量
        citNum = accoutOverViewCit[
            (accoutOverViewCit["记账条数"].astype(int) >= 40) & (accoutOverViewCit["未记天数"].astype(int) <= 8) & (
                    accoutOverViewCit["连续未记天数"].astype(int) <= 3)].shape[0]
        ## 农村数量
        conNum = accoutOverViewCout[
            (accoutOverViewCout["记账条数"].astype(int) >= 40) & (accoutOverViewCout["未记天数"].astype(int) <= 10) & (
                    accoutOverViewCout["连续未记天数"].astype(int) <= 5)].shape[0]
        ## 全体
        res.append(citNum + conNum)
        ## 城镇
        res.append(citNum)
        ## 农村
        res.append(conNum)
        ## 全体电子记账达标率（%）
        res.append(res[9] / res[3] * 100)
        ## 城镇电子记账户达标率（%）
        res.append(res[10] / res[4] * 100)
        ## 农村电子记账达标率（%）
        res.append(res[11] / res[5] * 100)

        # 未记账天数超过10天以上的户
        ## 户数
        res.append(elecDataDf[elecDataDf['未记天数'].astype(int) > 10].shape[0])
        ## 占比（%）
        res.append(res[15] / res[3] * 100)

        # 记账笔数少于40笔的户
        ## 户数
        res.append(elecDataDf[elecDataDf['记账条数'] < 40].shape[0])
        res.append(res[17] / res[3] * 100)

        # 户均记账天数情况
        ## 全体户均未记账天数
        res.append(elecDataDf['未记天数'].astype(float).sum() / res[3])
        ## 城镇户均未记账天数
        res.append(accoutOverViewCit['未记天数'].astype(float).sum() / res[4])
        ## 农村户均未记账天数
        res.append(accoutOverViewCout['未记天数'].astype(float).sum() / res[5])
        ## 全体户均连续未记账天数
        res.append(elecDataDf['连续未记天数'].astype(float).sum() / res[3])
        ## 城镇户均连续未记账天数
        res.append(accoutOverViewCit['连续未记天数'].astype(float).sum() / res[4])
        ## 农村户均连续未记账天数
        res.append(accoutOverViewCout['连续未记天数'].astype(float).sum() / res[5])

        # 记账笔数
        ## 全体户均记账笔数
        res.append(elecDataDf['记账条数'].astype(float).sum() / res[3])
        ## 城镇户均记账笔数
        res.append(accoutOverViewCit['记账条数'].astype(float).sum() / res[4])
        ## 农村户均记账笔数
        res.append(accoutOverViewCout['记账条数'].astype(float).sum() / res[5])

        # 月度间记账笔数相差50笔
        ## 全体
        res.append(comonHouses[comonHouses['记账笔数差'] > 50].shape[0])
        ## 城镇
        res.append(
            comonHouses[(~comonHouses['分省框城乡类别'].isin(['R'])) & (comonHouses['记账笔数差'] > 50)].shape[0])
        ## 农村
        res.append(comonHouses[(comonHouses['分省框城乡类别'].isin(['R'])) & (comonHouses['记账笔数差'] > 50)].shape[0])

        # 支收比
        ## 总支收比
        res.append(elecDataDf['支出合计'].sum() / elecDataDf['收入合计'].sum())
        ## 支收比大于1的户数 Nan 是分子 分母都为0
        elecDataDf['支收比'] = elecDataDf['支出合计'] / elecDataDf['收入合计']
        res.append(elecDataDf[(elecDataDf['支收比'] > 1) | elecDataDf['支收比'].isna()].shape[0])
        ## 占比
        res.append(res[32] / res[3] * 100)

        # 补记情况
        ## 15天的电子记账所有数据
        day15Df = accoutReviewEle[
            (accoutReviewEle['记账日期'].dt.month == self.month) & (accoutReviewEle['记账时间差'] > 15)]
        ## 补记前月的电子记账所有数据
        addtionDf = accoutReviewEle[
            (accoutReviewEle['记账日期'].dt.month == self.month) & (
                    accoutReviewEle['创建时间'].dt.month == (self.month + 1) % 12)]
        ## 3月记账时间与创建时间相差15天笔数
        res.append(day15Df.shape[0])
        ## 4月补记3月账目笔数
        res.append(addtionDf.shape[0])
        ## 3月记账时间与创建时间相差15天户数、
        res.append(day15Df.drop_duplicates(subset=['SID']).shape[0])
        ## 4月补记3月账目户数
        res.append(addtionDf.drop_duplicates(subset=['SID']).shape[0])
        return res

    def to_EleExcel(self, datas):
        """
        输出到excel中
        :param datas: 组装数据的二维数组
        :return:
        """
        fielName = self.calada[0:4] + '电子记账汇总.xlsx'
        # fullPath = './res/电子记账汇总/' + fielName
        fullPath = './结果输出/电子记账汇总/' + fielName
        if not os.path.exists(fullPath):
            # 不存在拷贝模板
            shutil.copyfile('./res/电子记账访户初始化数据/模板.xlsx', fullPath)

        # 加载数据
        mbwb = load_workbook(fullPath)
        # 获取所有的sheet，是否包含已经执行过得sheet
        if not str(self.month) + '月' in mbwb.sheetnames:
            # 获取模板sheet
            mbSheet = mbwb.copy_worksheet(mbwb['模板'])

            # 组装导出时间
            suppData = str(int(self.calada[4:6])) + '.' + str(int(self.calada[6:9]))
            # 数据值
            sheetTitleText = '全市' + str(self.month) + '月电子记账汇总情况表'
            suppInfoText = '补记情况（' + suppData + '导出）'
            preMonthDayText = str(self.month) + '月记账时间与创建时间相差15天笔数'
            preMonthHouseText = str(self.month) + '月记账时间与创建时间相差15天户数'
            nowMonthCountText = '' + str(self.preMonth) + '月补记' + str(self.month) + '月账目笔数'
            nowMonthCountHoueText = '' + str(self.preMonth) + '月补记' + str(self.month) + '月账目户数'

            # 设置值
            mbSheet.title = str(self.month) + '月'
            mbSheet.cell(1, 2).value = sheetTitleText
            mbSheet.cell(3, 36).value = suppInfoText
            mbSheet.cell(4, 36).value = preMonthDayText
            mbSheet.cell(4, 37).value = nowMonthCountText
            mbSheet.cell(4, 38).value = preMonthHouseText
            mbSheet.cell(4, 39).value = nowMonthCountHoueText

            # 写数据
            i = 5
            for data in datas:
                for j in range(len(data)):
                    mbSheet.cell(i, j + 2).value = data[j]
                i = i + 1
            # 保存
            mbwb.save(fullPath)
        else:
            return

    def initData(self):

        # 判断目录是否存在

        if not os.path.exists("./结果输出/电子记账汇总"):
            os.makedirs("./结果输出/电子记账汇总")

        # 遍历所有区县 获取二维数组数据
        datas = []
        for area in Area_Tools.area_objs:
            if (area['No'] == 510500):
                datas.append(self.getElecAccountSituation(self.accoutOverView, self.comonHouses, self.accoutReviewDf))
            else:
                datas.append(
                    self.getElecAccountSituation(self.accoutOverView[self.accoutOverView['COUN'] == area['No']],
                                                 self.comonHouses[self.comonHouses['COUN'] == area['No']],
                                                 self.accoutReviewDf[self.accoutReviewDf['COUN'] == str(area['No'])]))
        self.to_EleExcel(datas)
        QMessageBox.information(None, '提示', '执行完成')
        # print(datas)
